__all__ = ['Environment',
    'Meta', 'guess', 'noguess',
    'model', 'multi', 'one',
    'model_cr', 'model_cr_context',
    'cr', 'cr_context',
    'cr_uid', 'cr_uid_context',
    'cr_uid_id', 'cr_uid_id_context',
    'cr_uid_ids', 'cr_uid_ids_context',
    'cr_uid_records', 'cr_uid_records_context',
    'constrains', 'depends', 'onchange', 'returns',
    'call_kw',
]

import logging
from collections import defaultdict, Mapping
from contextlib import contextmanager
from inspect import currentframe, getargspec
from pprint import pformat
from weakref import WeakSet

from decorator import decorate, decorator
from werkzeug.local import Local, release_local

from odoo.tools import frozendict, classproperty, StackMap, pycompat
from odoo.exceptions import CacheMiss

_logger = logging.getLogger(__name__)

WRAPPED_ATTRS = ('__module__', '__name__', '__doc__', '_constrains',
                 '_depends', '_onchange', '_returns', 'clear_cache')
INHERITED_ATTRS = ('_returns',)

class Params(object):
    pass

class Meta(type):
    def __new__(meta, name, bases, attrs):
        parent = type.__new__(meta, name, bases, {})

        for key, value in list(attrs.items()):
            if not key.startswith('__') and callable(value):
                value = propagate(getattr(parent, key, None), value)
                if not hasattr(value, '_api'):
                    try:
                        value = guess(value)
                    except TypeError:
                        pass
                if (getattr(value, '_api', None) or '').startswith('cr'):
                    _logger.warning("Deprecated method %s.%s in module %s", name, key, attrs.get('__module__'))

                attrs[key] = value

        return type.__new__(meta, name, bases, attrs)


def attrsetter(attr, value):
    return lambda method: setattr(method, attr, value) or method

def propagate(method1, method2):
    if method1:
        for attr in INHERITED_ATTRS:
            if hasattr(method1, attr) and not hasattr(method2, attr):
                setattr(method2, attr, getattr(method1, attr))

    return method2

def constrains(*args):
    pass

def onchange(*args):
    pass

def depends(*args):
    if args and callable(args[0]):
        pass
    elif any('id' in arg.split('.') for arg in args):
        pass
    return attrsetter('_depends', args)

def returns(model, downgrade=None, upgrade=None):
    return attrsetter('_returns', (model, downgrade, upgrade))

def downgrade(method, value, self, args, kwargs):
    pass

def aggregate(method, value, self):
    pass

def split_context(method, args, kwargs):
    pass

def model(method):
    if method.__name__ == 'create':
        return model_create_single(method)
    method._api = 'model'
    return method

def multi(method: object) -> object:
    method._api = 'multi'
    return method

def one(method):
    pass

def model_cr(method):
    method._api = 'model_cr'
    return method

def model_cr_context(method):
    method._api = 'model_cr_context'
    return method

_create_logger = logging.getLogger(__name__ + '.create')
def _model_create_single(create, self, arg):
    pass

def model_create_single(method):
    wrapper = decorate(method, _model_create_single)
    wrapper._api = 'model_create'
    return wrapper

def _model_create_multi(create, self, arg):
    pass

def model_create_multi(method):
    pass

def cr(method):
    pass

def cr_context(method):
    pass

def cr_uid(method):
    pass

def cr_uid_context(method):
    pass

def cr_uid_id(method):
    pass

def cr_uid_id_context(method):
    pass

def cr_uid_ids(method):
    pass

def cr_uid_ids_context(method):
    pass

def cr_uid_records(method):
    pass

def cr_uid_records_context(method):
    pass

def v7(method_v7):
    pass

def v8(method_v8):
    pass

def noguess(method):
    method._api = None
    return method

def guess(method):
    if hasattr(method, '_api'):
        return method

    args, vname, kwname, defaults = getargspec(method)
    names = tuple(args) + (None,) * 4

    if names[0] == 'self':
        if names[1] in ('cr', 'cursor'):
            if names[2] in ('uid', 'user'):
                if names[3] == 'ids':
                    if 'context' in names or kwname:
                        return cr_uid_ids_context(method)
                    else:
                        return cr_uid_ids(method)
                elif names[3] == 'id' or names[3] == 'res_id':
                    if 'context' in names or kwname:
                        return cr_uid_id_context(method)
                    else:
                        return cr_uid_id(method)
                elif 'context' in names or kwname:
                    return cr_uid_context(method)
                else:
                    return cr_uid(method)
            elif 'context' in names:
                return cr_context(method)
            else:
                return cr(method)

    return noguess(method)

def expected(decorator, func):
    pass

def _call_kw_model(method, self, args, kwargs):
    pass

def _call_kw_model_create(method, self, args, kwargs):
    pass

def _call_kw_multi(method, self, args, kwargs):
    pass

def call_kw(model, name, args, kwargs):
    pass

class Environment(Mapping):
    _local = Local()

    @classproperty
    def envs(cls):
        pass

    @classmethod
    @contextmanager
    def manage(cls):
        if hasattr(cls._local, 'environments'):
            yield
        else:
            try:
                cls._local.environments = Environments()
                yield
            finally:
                release_local(cls._local)

    @classmethod
    def reset(cls):
        pass

    def __new__(cls, cr, uid, context):
        pass

    def __contains__(self, model_name):
        pass

    def __getitem__(self, model_name):
        pass

    def __iter__(self):
        pass

    def __len__(self):
        pass

    def __eq__(self, other):
        pass

    def __ne__(self, other):
        pass

    def __hash__(self):
        pass

    def __call__(self, cr=None, user=None, context=None):
        pass

    def ref(self, xml_id, raise_if_not_found=True):
        pass

    @property
    def user(self):
        pass

    @property
    def lang(self):
        pass

    @contextmanager
    def _do_in_mode(self, mode):
        pass

    def do_in_draft(self):
        pass

    @property
    def in_draft(self):
        pass

    def do_in_onchange(self):
        pass

    @property
    def in_onchange(self):
        pass

    def clear(self):
        pass

    @contextmanager
    def clear_upon_failure(self):
        pass

    def protected(self, field):
        pass

    @contextmanager
    def protecting(self, what, records=None):
        pass

    def field_todo(self, field):
        pass

    def check_todo(self, field, record):
        pass

    def add_todo(self, field, records):
        pass

    def remove_todo(self, field, records):
        pass

    def has_todo(self):
        pass

    def get_todo(self):
        pass

    @property
    def recompute(self):
        pass

    @contextmanager
    def norecompute(self):
        pass

    def cache_key(self, field):
        pass

class Environments(object):
    def __init__(self):
        self.envs = WeakSet()
        self.cache = Cache()
        self.todo = {}  # recomputations {field: [records]}
        self.mode = False  # flag for draft/onchange
        self.recompute = True

class Cache(object):
    def __init__(self):
        self._data = defaultdict(lambda: defaultdict(dict))

    def contains(self, record, field):
        pass

    def get(self, record, field):
        pass

    def set(self, record, field, value):
        pass

    def update(self, records, field, values):
        pass

    def remove(self, record, field):
        pass

    def contains_value(self, record, field):
        pass

    def get_value(self, record, field, default=None):
        pass

    def get_special(self, record, field, default=None):
        pass

    def set_special(self, record, field, getter):
        pass

    def set_failed(self, records, fields, exception):
        pass

    def get_fields(self, record):
        pass

    def get_records(self, model, field):
        pass

    def get_missing_ids(self, records, field):
        pass

    def copy(self, records, env):
        pass

    def invalidate(self, spec=None):
        pass

    def check(self, env):
        pass

class SpecialValue(object):
    __slots__ = ['get']

    def __init__(self, getter):
        pass

from odoo import SUPERUSER_ID
from odoo.exceptions import UserError, AccessError, MissingError
from odoo.modules.registry import Registry